package application

import (
	"context"

	"github.com/samber/do/v2"
	"gorm.io/gorm"

	"gitee.com/monkeyPP/happy-mall/pkg/log"
	"gitee.com/monkeyPP/happy-mall/services/productservice/internal/domain/entity"
	"gitee.com/monkeyPP/happy-mall/services/productservice/internal/domain/repository"
	"gitee.com/monkeyPP/happy-mall/services/productservice/internal/domain/service"
	"gitee.com/monkeyPP/happy-mall/services/productservice/internal/infrastructure/eventbus"
	"gitee.com/monkeyPP/happy-mall/services/productservice/internal/interfaces/api/request"
)

type ProductApp interface {
	AddProduct(context.Context, *request.AddProductRequest) (*entity.Product, error)
}

type ProductAppImpl struct {
	productService service.ProductService
	productRepo    repository.ProductRepository
	skuRepo        repository.SkuRepository
	eventBus       eventbus.EventBus
	db             *gorm.DB
}

func NewProductApp(i do.Injector) (ProductApp, error) {
	productService := do.MustInvoke[service.ProductService](i)
	productRepo := do.MustInvoke[repository.ProductRepository](i)
	skuRepo := do.MustInvoke[repository.SkuRepository](i)
	eventBus := do.MustInvoke[eventbus.EventBus](i)
	db := do.MustInvoke[*gorm.DB](i)
	return &ProductAppImpl{
		productService: productService,
		productRepo:    productRepo,
		skuRepo:        skuRepo,
		eventBus:       eventBus,
		db:             db,
	}, nil
}

func (p *ProductAppImpl) AddProduct(ctx context.Context, addProductRequest *request.AddProductRequest) (*entity.Product, error) {
	log.C(ctx).Infow("ProductApp.AddProduct", "req", addProductRequest)

	product, err := p.productService.ProcessAddProduct(ctx, addProductRequest)

	tx := p.db.Begin()
	err = p.productRepo.Save(ctx, tx, product)
	if err != nil {
		log.C(ctx).Infow("ProductAppImpl.AddProduct", "save product err", err)
		tx.Rollback()
		return nil, err
	}
	err = p.skuRepo.BatchSave(ctx, tx, product.SkuList)
	if err != nil {
		log.C(ctx).Infow("ProductAppImpl.AddProduct", "save sku err", err)
		tx.Rollback()
		return nil, err
	}

	err = p.eventBus.Publish(ctx, &eventbus.AddProductEvent{
		ProductInfo: product,
	})
	if err != nil {
		log.C(ctx).Infow("ProductServiceImpl.ProcessAddProduct", "publish event err", err)
		tx.Rollback()
		return nil, err
	}
	tx.Commit()
	return product, err
}
